package jp.android_group.attender.android.ui;

/*
 * Copyright (c) 2009, "Japan Android Group Attender Team"
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

import java.io.IOException;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;

import jp.android_group.attender.android.R;
import jp.android_group.attender.android.entity.Attendee;
import jp.android_group.attender.android.entity.Event;
import jp.android_group.attender.android.entity.ServerEntry;
import jp.android_group.attender.android.net.IServerInterface;
import jp.android_group.attender.android.net.ServerInterfaceFactory;

import org.apache.http.client.HttpResponseException;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnDismissListener;
import android.database.Cursor;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View.OnClickListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.TextView.OnEditorActionListener;

/**
 * 出席者リスト表示画面
 * 
 */
public class AttendeeListActivity extends BaseActivity implements
		OnClickListener, OnEditorActionListener {
	public static final String LOG_TAG = "AttendeeListActivity";
	public static final boolean DEBUG_FLG = true;

	// 定数：リクエストコード
	public static final int REQUEST_CODE = 0x583426fa;

	// 定数：結果コード - 設定アクティビティ起動のため終了する
	public static final int RESULT_LAUNCH_LOGIN_SETTING = 0x1245afa;

	// 定数：再試行の回数
	public static final int RETRY_NUMBER = 4;
	public static final int RETRY_WAIT_TIME = 2;

	IServerInterface mServerInterface = null;
	ServerEntry mServerEntry = null;
	private volatile boolean mCancelFlg = false;
	private final Timer mTimer = new Timer();

	// データベースアダプタ
	private CursorAdapter mCursorAdapter = null;

	private Attendee.Filter mFilter = null;
	private int mAttendeeOrder = Attendee.ORDER_ID_ASC;

	private Dialog mDialogConfirm = null;
	private ProgressBar mPbConfirmRemainTime = null;

	// イベント情報
	private Event mEventData = null;

	private AlertDialog mAlertDialog = null;

	private Cursor mCursor = null;

	// mCursorを入れ替える際にsynchronizedするロック用オブジェクト
	private volatile Object mCursorLock = new Object();

	/**
	 * 参加者リストをサーバーに送信
	 */
	private Runnable mUploadAttendeeList = new Runnable() {
		public void run() {

			synchronized (mDb) {
				if (mModeOffline == false) {

					Cursor cursor = Attendee
							.loadAttendeeCursor(mDb, mEventData);

					for (int i = 0; i < RETRY_NUMBER; i++) {

						boolean retryFlg = false;
						try {
							// アップロード
							mServerInterface.uploadAttendees(mEventData,
									cursor, mDb);
							mHandler.sendEmptyMessage(HANDLE_RELOAD_LIST);

						} catch (final HttpResponseException e) {
							retryFlg = true;
							showToastException(e);

						} catch (final IOException e) {
							retryFlg = true;
							showToastException(e);
						}

						if (retryFlg == false) {
							break;
						}
					}
				}

			} // synchronized(mDb)
		}
	};
	// 画面の要素
	private ListView mLVAttendeeList = null;

	// 音声再生用プレイヤー
	private MediaPlayer mSoundPlayerSad = null;

	private ProgressDialog mProgressDialog = null;
	private PowerManager.WakeLock mWakeLock = null;
	private Handler mHandler = new Handler() {
		public void handleMessage(Message msg) {
			switch (msg.what) {

			case HANDLE_SHOW_PROGRESS_DIALOG:
				synchronized (mProgressDialog) {
					mProgressDialog
							.setMessage(getText(R.string.retrieving_attendee_list));
					mProgressDialog.show();
				}
				break;

			case HANDLE_DISMISS_PROGRESS_DIALOG:
				synchronized (mProgressDialog) {
					if (mProgressDialog.isShowing())
						mProgressDialog.dismiss();
				}
				break;

			case HANDLE_SET_RETRYING_MESSAGE_PROGRESS_DIALOG:
				mProgressDialog.setMessage(getText(R.string.retrying));
				break;

			case HANDLE_RELOAD_LIST:
				synchronized (mDb) {
					if (mDb.isOpen() == false)
						return;
					synchronized (mCursorLock) {
						mCursor = Attendee.loadCursor(mDb, mEventData, mFilter,
								mAttendeeOrder);
						mCursorAdapter.changeCursor(mCursor);
					}
					// IllegalStateExceptionが発生
					// mCursor.requery();
				}
				break;

			case HANDLE_SHOW_ATTENDEE_SENDING_PROGRESS:
				initProgressDialog(HANDLE_SHOW_ATTENDEE_SENDING_PROGRESS);
				mProgressDialog.show();
				break;

			case HANDLE_SHOW_CANNOT_SENT_ALERT:
				initAlertDialog(HANDLE_SHOW_CANNOT_SENT_ALERT);
				mAlertDialog.show();
				break;

			case HANDLE_START_EVENT_SELECT_ACTIVITY:
				startEventSelectActivity();
				break;

			case HANDLE_UPDATE_TITLE:
				updateTitle();
				break;
			}
			;
		}
	};

	private static final int HANDLE_UPDATE_TITLE = 0x49847;
	private static final int HANDLE_SHOW_PROGRESS_DIALOG = 0x9835;
	private static final int HANDLE_DISMISS_PROGRESS_DIALOG = 0x573457;
	private static final int HANDLE_SET_RETRYING_MESSAGE_PROGRESS_DIALOG = 0x8402;
	private static final int HANDLE_RELOAD_LIST = 0x4517857;
	private static final int HANDLE_START_EVENT_SELECT_ACTIVITY = 0x98742;
	final int HANDLE_SHOW_ATTENDEE_SENDING_PROGRESS = 0x897345;
	final int HANDLE_SET_PROGRESS_BAR_INDETERMINATE = 0x879845;
	private static final int HANDLE_SHOW_CANNOT_SENT_ALERT = 0x897023;

	/**
	 * 
	 */
	private void updateTitle() {
		if (mModeOffline == false) {
			setTitle(mEventData.getName());
		} else {
			setTitle(mEventData.getName() + " - " + getText(R.string.offline));
		}
	}

	/**
	 * アラートダイアログの設定
	 * 
	 * @param type
	 */
	private void initProgressDialog(int type) {
		switch (type) {
		case HANDLE_SHOW_ATTENDEE_SENDING_PROGRESS:
			mProgressDialog.setTitle(R.string.attendee_sending_title);
			mProgressDialog.setMessage(AttendeeListActivity.this.getResources()
					.getText(R.string.attendee_sending));
			break;

		}
		;
	}

	/**
	 * アラートダイアログの設定
	 * 
	 * @param type
	 */
	private void initAlertDialog(int type) {
		switch (type) {
		case HANDLE_SHOW_CANNOT_SENT_ALERT:
			mAlertDialog.setTitle(R.string.attendee_info_cannot_sent_title);
			mAlertDialog
					.setMessage(getText(R.string.attendee_info_cannot_sent));
			mAlertDialog.setButton(getText(R.string.ok),
					new Dialog.OnClickListener() {
						@Override
						public void onClick(DialogInterface dialog, int which) {
						}
					});
			break;
		}
		;
	}

	private boolean searchBoxIsShown = false;

	/**
	 * 検索ボックスの表示
	 */
	private final void showSearchBox() {
		searchBoxIsShown = true;
		findViewById(R.id.search_layout).setVisibility(View.VISIBLE);
		findViewById(R.id.search_layout_et_term).requestFocus();

	}

	/**
	 * 検索ボックスの非表示
	 */
	private final void dismissSearchBox() {
		searchBoxIsShown = false;
		findViewById(R.id.search_layout).setVisibility(View.GONE);

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onCreate(android.os.Bundle)
	 */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		if (DEBUG_FLG)
			Log.d(LOG_TAG, "onCreate");

		mWakeLock = ((PowerManager) getSystemService(POWER_SERVICE))
				.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK,
						AttendeeListActivity.class.getName());
		mWakeLock.acquire();

		setContentView(R.layout.attendee_list);

		// 検索ボックスの消去
		dismissSearchBox();
		findViewById(R.id.search_layout_btn_exe).setOnClickListener(this);
		((EditText) findViewById(R.id.search_layout_et_term))
				.setOnEditorActionListener(this);

		mLVAttendeeList = (ListView) findViewById(R.id.attendee_list_lv_attendee);
		mLVAttendeeList
				.setOnItemClickListener(mAttendeeListOnItemClickListener);
		mLVAttendeeList
				.setOnItemLongClickListener(mAttendeeListOnItemLongClickListener);

		mProgressDialog = new ProgressDialog(this);
		mProgressDialog.setOnDismissListener(new OnDismissListener() {
			@Override
			public void onDismiss(DialogInterface dialog) {
				mCancelFlg = true;
			}
		});

		// サウンド再生用
		mSoundPlayerSad = MediaPlayer.create(this, R.raw.sad);

		mAlertDialog = mAlertDialogBuilder.create();

		if (DEBUG_FLG)
			Log.d(LOG_TAG, "onCreate completed.");

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onResume()
	 */
	@Override
	public void onResume() {
		super.onResume();

		// mHandler.sendEmptyMessage(HANDLE_RELOAD_LIST);

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see jp.android_group.attender.android.ui.BaseActivity#onStart()
	 */
	@Override
	public void onStart() {
		super.onStart();

		// 初期化処理
		if (mQrScannerLaunchedFlg == false) {
			new Thread(mInitialize).start();
		} else {
			mQrScannerLaunchedFlg = true;
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onResume()
	 */
	@Override
	public void onPause() {
		super.onPause();

		if (mModeOffline != true) {

			synchronized (mDb) {

				Cursor cursor = Attendee.loadAttendeeCursor(mDb, mEventData);

				// 未送信の出席者情報があれば、
				if (mQrScannerLaunchedFlg == false && (cursor.getCount() > 0)) {

					Toast.makeText(
							this,
							this.getResources().getText(
									R.string.upload_process_was_started),
							Toast.LENGTH_SHORT).show();

					// 出席者リストのアップロード
					new Thread(mUploadAttendeeList).start();

				}
			}
		}
		if (DEBUG_FLG)
			Log.d(LOG_TAG, "onPause completed.");

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see jp.android_group.attender.android.ui.BaseActivity#onStop()
	 */
	@Override
	public void onStop() {
		super.onStop();
		if (DEBUG_FLG)
			Log.d(LOG_TAG, "onStop");

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onDestroy()
	 */
	@Override
	public void onDestroy() {
		super.onDestroy();
		mWakeLock.release();
	}

	/**
	 * 
	 * @param attendeeData
	 * @param view
	 */
	private void showAttendCancelDialog(final Attendee attendeeData,
			final View view) {
		AlertDialog ad = mAlertDialogBuilder.create();
		ad.setMessage(getResources().getText(
				R.string.would_you_like_to_undo_attend));
		ad.setTitle(getResources().getText(R.string.undo_attend));
		ad.setButton(getResources().getText(R.string.yes),
				new Dialog.OnClickListener() {
					@Override
					public void onClick(DialogInterface arg0, int arg1) {

						attendeeData.setAttendFlg(Attendee.ATTEND_FLG_NOT_SET);
						synchronized (mDb) {
							Attendee.revertAttend(attendeeData, mDb);
						}
						mHandler.sendEmptyMessage(HANDLE_RELOAD_LIST);

						arg0.dismiss();
					}
				});
		ad.setButton2(getResources().getText(R.string.no),
				new Dialog.OnClickListener() {
					@Override
					public void onClick(DialogInterface arg0, int arg1) {
						arg0.dismiss();
					}
				});
		ad.show();
	}

	/**
	 * 参加登録の取り消しが出来ない ダイアログを表示
	 */
	void showDialogCannotCancelAttend() {

		AlertDialog ad = mAlertDialogBuilder.create();
		ad.setTitle(getResources().getText(R.string.undo_attend));
		ad.setMessage(getResources().getText(R.string.you_cannot_undo_attend));
		ad.setButton(getResources().getText(R.string.ok),
				new Dialog.OnClickListener() {
					@Override
					public void onClick(DialogInterface arg0, int arg1) {
						arg0.dismiss();
					}
				});
		ad.show();

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onActivityResult(int, int,
	 * android.content.Intent)
	 */
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (DEBUG_FLG)
			Log.d(LOG_TAG, "onActivityResult");

		openDatabase();

		switch (requestCode) {

		// イベント情報の設定
		case EventListActivity.REQUEST_CODE:
			if (DEBUG_FLG)
				Log.d(LOG_TAG, "activity result is EventListActivity");

			if (resultCode == Activity.RESULT_OK) {
				if (data.hasExtra(Event.class.getName())) {
					mEventData = (Event) data.getSerializableExtra(Event.class
							.getName());
				}
			}
			break;

		// 認証情報の初期設定
		case ServerSettingActivity.REQUEST_CODE_INIT:
			if (DEBUG_FLG)
				Log.d(LOG_TAG, "activity result is LoginActivity");

			if (resultCode == Activity.RESULT_OK) {
				setResult(Activity.RESULT_CANCELED);
				finish();
			}
			break;

		// ＱＲコードをスキャン
		case REQUEST_CODE_QR_SCAN:

			if (resultCode == RESULT_OK) {
				String contents = data.getStringExtra("SCAN_RESULT");

				Attendee attendee = null;
				synchronized (mDb) {
					attendee = Attendee.findByAttendeeId(mEventData, contents,
							mDb);
				}

				if (attendee != null) {

					// 正常処理の通知
					// TODO: 正常処理の通知を実装
					// 音を鳴らす等

					if (attendee.getAttendFlg() == Attendee.ATTEND_FLG_NOT_SET) {
						attendee.setAttendFlg(Attendee.ATTEND_FLG_ATTEND);

						Attendee.save(attendee, mDb);
						mHandler.sendEmptyMessage(HANDLE_RELOAD_LIST);

					}

					// 確認ダイアログの表示
					showDialogConfirm(attendee);

				} else {

					mSoundPlayerSad.start();

					// ダイアログ表示：出席予定者リストにない
					showDialogAttendeeNotFoundInList();
				}

			} else if (resultCode == Activity.RESULT_CANCELED) {

			}
			break;
		}

		if (DEBUG_FLG)
			Log.d(LOG_TAG, "onActivityResult completed.");

	}

	// 参加者リストの行をタッチした際の処理
	private OnItemClickListener mAttendeeListOnItemClickListener = new OnItemClickListener() {

		@Override
		public void onItemClick(AdapterView<?> arg0, View view, int position,
				long arg3) {

			Attendee data = new Attendee();
			Attendee.fetchData((Cursor) arg0.getItemAtPosition(position), data);
			switch (data.getAttendFlg()) {

			case Attendee.ATTEND_FLG_NOT_SET:
				// 出席に変更
				data.setAttendFlg(Attendee.ATTEND_FLG_ATTEND);
				Attendee.save(data, mDb);
				mHandler.sendEmptyMessage(HANDLE_RELOAD_LIST);
				break;
			}
			;
		}
	};

	// 参加者リストの行をロングタッチした際の処理
	private OnItemLongClickListener mAttendeeListOnItemLongClickListener = new OnItemLongClickListener() {

		@Override
		public boolean onItemLongClick(AdapterView<?> arg0, View view,
				int position, long arg3) {

			Attendee data = new Attendee();
			Attendee.fetchData((Cursor) arg0.getItemAtPosition(position), data);

			switch (data.getAttendFlg()) {

			// 参加フラグセット済み
			case Attendee.ATTEND_FLG_ATTEND:
				// ダイアログ表示：参加のキャンセル
				showAttendCancelDialog(data, view);
				break;

			// サーバー送信済み
			case Attendee.ATTEND_FLG_REGISTERED:
				// ダイアログ表示：参加のキャンセルが出来ない
				showDialogCannotCancelAttend();
				break;
			}
			;

			return true;
		}
	};

	/**
	 * 参加者情報が見つからない ダイアログ表示
	 */
	private void showDialogAttendeeNotFoundInList() {
		AlertDialog dialog = mAlertDialogBuilder.create();
		dialog.setTitle(R.string.error_has_occured);
		dialog.setMessage(getResources().getText(
				R.string.it_is_maybe_not_attendee));
		dialog.setButton(getResources().getText(R.string.ok),
				new Dialog.OnClickListener() {
					@Override
					public void onClick(DialogInterface arg0, int arg1) {
						arg0.dismiss();
					}
				});
		dialog.show();
	}

	/**
	 * 確認ダイアログの表示
	 * 
	 * @param data
	 */
	void showDialogConfirm(Attendee data) {

		if (mDialogConfirm == null) {
			mDialogConfirm = new Dialog(this);
			mDialogConfirm.setCancelable(false);
			mDialogConfirm.setTitle(R.string.attend_confirmed);
			mDialogConfirm.setContentView(R.layout.confirm_dialog);
			mDialogConfirm.setOnDismissListener(new OnDismissListener() {
				@Override
				public void onDismiss(DialogInterface dialog) {
				}
			});

			// 進行表示
			mPbConfirmRemainTime = (ProgressBar) mDialogConfirm
					.findViewById(R.id.confirm_dialog_pb_remain);
			mPbConfirmRemainTime.setMax(10);

			((Button) mDialogConfirm
					.findViewById(R.id.confirm_dialog_btn_continue))
					.setOnClickListener(new OnClickListener() {
						@Override
						public void onClick(View v) {

							if (mWaitingTimer != null)
								mWaitingTimer.cancel();

							if (mDialogConfirm.isShowing())
								mDialogConfirm.dismiss();

							// QRリーダーの起動
							startQRCodeReader();
						}
					});

		}

		Button btnStop = (Button) mDialogConfirm
				.findViewById(R.id.confirm_dialog_btn_pause);
		btnStop.setText(R.string.pause);
		btnStop.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {

				mPbConfirmRemainTime.setVisibility(View.INVISIBLE);
				Button btn = (Button) v;
				btn.setText(R.string.close);

				// クリックイベントの変更
				// ダイアログを閉じる
				btn.setOnClickListener(new OnClickListener() {
					@Override
					public void onClick(View v) {
						if (mDialogConfirm.isShowing())
							mDialogConfirm.dismiss();
						mHandler.sendEmptyMessage(HANDLE_RELOAD_LIST);
					}
				});
				if (mWaitingTimer != null)
					mWaitingTimer.cancel();

			}
		});

		mPbConfirmRemainTime.setEnabled(true);
		mPbConfirmRemainTime.setProgress(10);
		mPbConfirmRemainTime.setVisibility(View.VISIBLE);

		((TextView) mDialogConfirm.findViewById(R.id.confirm_dialog_tv_name))
				.setText(data.getName());
		((TextView) mDialogConfirm.findViewById(R.id.confirm_dialog_tv_comment))
				.setText(data.getComment());

		mWaitingTimer = getWaitingTimerTask();
		mTimer.schedule(mWaitingTimer, 0, 1000);
		mDialogConfirm.show();
	}

	TimerTask mWaitingTimer = null;

	private TimerTask getWaitingTimerTask() {

		return new TimerTask() {

			@Override
			public void run() {
				int progress = mPbConfirmRemainTime.getProgress();
				if (progress > 0) {
					mPbConfirmRemainTime.setProgress(progress - 1);
				} else {
					mDialogConfirm.dismiss();

					// タイマのキャンセル
					this.cancel();

					// QRリーダーの起動
					startQRCodeReader();
				}
			}
		};
	}

	/**
	 * イベント選択アクティビティ起動
	 */
	private void startEventSelectActivity() {
		Intent intent = new Intent(this, EventListActivity.class);
		intent.addCategory(Intent.CATEGORY_DEFAULT);
		intent.setAction(Intent.ACTION_EDIT);
		startActivityForResult(intent, EventListActivity.REQUEST_CODE);
	}

	/**
	 * 初期化処理
	 */
	Runnable mInitialize = new Runnable() {

		public void run() {
			if (DEBUG_FLG)
				Log.d(LOG_TAG, "initializing...");

			// サーバー情報
			if (getIntent().hasExtra(ServerEntry.class.getName())) {
				mServerEntry = (ServerEntry) getIntent().getSerializableExtra(
						ServerEntry.class.getName());
				mServerInterface = ServerInterfaceFactory
						.getInterface(mServerEntry);
			}

			// イベント情報
			if (getIntent().hasExtra(Event.class.getName())) {
				mEventData = (Event) getIntent().getSerializableExtra(
						Event.class.getName());
				mHandler.sendEmptyMessage(HANDLE_UPDATE_TITLE);
			} else {
				// イベント選択アクティビティ起動
				mHandler.sendEmptyMessage(HANDLE_START_EVENT_SELECT_ACTIVITY);
				return;
			}

			mFilter = new Attendee.Filter();
			mHandler.post(new Runnable() {
				@Override
				public void run() {
					synchronized (mCursorLock) {
						synchronized (mDb) {
							if (mDb.isOpen() == false)
								return;
							mCursor = Attendee.loadCursor(mDb, mEventData,
									mFilter, mAttendeeOrder);
						}
						mCursorAdapter = new ListAdapter(mCursor);
						mLVAttendeeList.setAdapter(mCursorAdapter);
					}
				}
			});

			if (DEBUG_FLG)
				Log.v(LOG_TAG, "mGetAttendeeData start.");
			new Thread(mGetAttendeeData).start();

			if (DEBUG_FLG)
				Log.d(LOG_TAG, "initialize process is completed.");
		}
	};

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * jp.android_group.attender.android.ui.BaseActivity#willConfirmWhenClosing
	 * ()
	 */
	@Override
	protected boolean willConfirmWhenClosing() {
		return true;
	}

	Runnable mGetAttendeeData = new Runnable() {
		public void run() {

			synchronized (mDb) {
				if (mModeOffline != true) {
					mCancelFlg = false;

					if (DEBUG_FLG)
						Log.v(LOG_TAG, "loading attendee data... ");

					// 進行中ダイアログ表示
					mHandler.sendEmptyMessage(HANDLE_SHOW_PROGRESS_DIALOG);

					// 参加者リスト読込み
					for (int i = 0; i < RETRY_NUMBER && mCancelFlg == false; i++) {
						boolean retryFlg = false;

						// 参加者リストの読み込み
						try {
							mServerInterface.getAttendees(mEventData, mDb);

							mEventData.setLastRetrieveDate(Calendar
									.getInstance().getTimeInMillis());
							Event.save(mEventData, mDb);

						} catch (final HttpResponseException e) {
							if (DEBUG_FLG)
								Log.d(LOG_TAG, e.getLocalizedMessage());
							showToastException(e);
							retryFlg = true;
						} catch (final IOException e) {
							showToastException(e);
							retryFlg = true;
						}

						if (retryFlg == false)
							break;

						try {
							if (DEBUG_FLG)
								Log.d(LOG_TAG, "attendee list canot retrieve.");

							mHandler
									.sendEmptyMessage(HANDLE_SET_RETRYING_MESSAGE_PROGRESS_DIALOG);

							Thread.sleep(RETRY_WAIT_TIME * 1000);
						} catch (final InterruptedException e) {
							showToastException(e);
						}

					}
				}
				mHandler.sendEmptyMessage(HANDLE_DISMISS_PROGRESS_DIALOG);

			}

			mHandler.sendEmptyMessage(HANDLE_RELOAD_LIST);

		}
	};

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onTrackballEvent(android.view.MotionEvent)
	 */
	@Override
	public boolean onTrackballEvent(MotionEvent event) {

		if (event.getAction() == MotionEvent.ACTION_DOWN) {
			if (DEBUG_FLG)
				Log.d(LOG_TAG, "trackball is pushed.");

			// ＱＲコードリーダ起動
			startQRCodeReader();

			return true;
		}

		return super.onTrackballEvent(event);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onKeyDown(int, android.view.KeyEvent)
	 */
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {

		switch (event.getKeyCode()) {
		case KeyEvent.KEYCODE_BACK:
			if (searchBoxIsShown) {
				dismissSearchBox();
				return false;
			}
			break;
		case KeyEvent.KEYCODE_SEARCH:
			if (searchBoxIsShown) {
				dismissSearchBox();
			} else {
				showSearchBox();
			}
			return true;
		}
		;

		return super.onKeyDown(keyCode, event);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * jp.android_group.attender.android.ui.BaseActivity#changeModeOffline(boolean
	 * )
	 */
	@Override
	protected void changeModeOffline(boolean mode) {
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onPrepareOptionsMenu(android.view.Menu)
	 */
	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		menu.findItem(R.id.attendee_list_menu_commit).setEnabled(
				!(mModeOffline));

		MenuItem item = menu.findItem(R.id.attendee_list_menu_search);
		if (searchBoxIsShown) {
			item.setTitle(getText(R.string.search_dismiss));
		} else {
			item.setTitle(getText(R.string.search));
		}

		return super.onPrepareOptionsMenu(menu);
	}

	/**
	 * 
	 * @param order
	 */
	private void setOrder(int order) {
		mAttendeeOrder = order;
		mHandler.sendEmptyMessage(HANDLE_RELOAD_LIST);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
	 */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.attendee_list, menu);

		MenuItem menuItem = null;

		menuItem = menu.findItem(R.id.attendee_list_menu_commit);
		menuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
			@Override
			public boolean onMenuItemClick(MenuItem item) {
				Toast.makeText(AttendeeListActivity.this,
						getText(R.string.upload_process_was_started),
						Toast.LENGTH_LONG).show();

				// 出席者リストのアップロード
				if (mModeOffline != true) {
					new Thread(mUploadAttendeeList).start();
				}

				return true;
			}
		});

		menuItem = menu.findItem(R.id.attendee_list_menu_search);
		menuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
			@Override
			public boolean onMenuItemClick(MenuItem item) {
				if (searchBoxIsShown) {
					dismissSearchBox();
				} else {
					showSearchBox();
				}
				return true;
			}
		});

		menuItem = menu.findItem(R.id.attendee_list_menu_sort_number);
		menuItem.setEnabled(false);
		menuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
			@Override
			public boolean onMenuItemClick(MenuItem item) {
				return true;
			}
		});

		menuItem = menu.findItem(R.id.attendee_list_menu_sort_name);
		menuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
			@Override
			public boolean onMenuItemClick(MenuItem item) {
				setOrder(Attendee.ORDER_NAME_ASC);
				return true;
			}
		});

		menuItem = menu.findItem(R.id.attendee_list_menu_sort_mail_address);
		menuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
			@Override
			public boolean onMenuItemClick(MenuItem item) {
				if (DEBUG_FLG)
					Log.d(LOG_TAG, "sort as mail address.");
				setOrder(Attendee.ORDER_MAIL_ADDRESS_ASC);
				return true;
			}
		});

		menuItem = menu.findItem(R.id.attendee_list_menu_setting);
		menuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
			@Override
			public boolean onMenuItemClick(MenuItem item) {

				AttendeeListActivity.this
						.setResult(RESULT_LAUNCH_LOGIN_SETTING);
				AttendeeListActivity.this.finish();

				return true;
			}
		});

		menuItem = menu.findItem(R.id.attendee_list_menu_about);
		menuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
			@Override
			public boolean onMenuItemClick(MenuItem item) {

				showAboutDialog();
				return false;
			}
		});

		return super.onCreateOptionsMenu(menu);
	}

	/**
	 * リストアダプタ
	 */
	class ListAdapter extends CursorAdapter {

		/**
		 * コンストラクタ
		 * 
		 * @param filter
		 * @param cursor
		 */
		ListAdapter(Cursor cursor) {
			super(AttendeeListActivity.this, cursor);
		}

		@Override
		public void bindView(View view, Context context, Cursor cursor) {
			((TextView) view
					.findViewById(R.id.attendee_list_row_tv_attendee_name))
					.setText(cursor.getString(Attendee.COLUMN_NAME));
			((TextView) view
					.findViewById(R.id.attendee_list_row_tv_attendee_mail_address))
					.setText(cursor.getString(Attendee.COLUMN_MAIL_ADDRESS));

			CharSequence status = null;
			int attendFlg = cursor.getInt(Attendee.COLUMN_ATTEND_FLAG);

			if (attendFlg == Attendee.ATTEND_FLG_NOT_SET) {
				status = getText(R.string.status_not_set);
			} else if (attendFlg == Attendee.ATTEND_FLG_ATTEND) {
				status = getText(R.string.status_attend);
			} else if (attendFlg == Attendee.ATTEND_FLG_REGISTERED) {
				status = getText(R.string.status_reistered);
			}

			((TextView) view
					.findViewById(R.id.attendee_list_row_tv_attenee_status))
					.setText(status);
		}

		@Override
		public View newView(Context context, Cursor cursor, ViewGroup parent) {
			return View.inflate(AttendeeListActivity.this,
					R.layout.attendee_list_row, null);
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.view.View.OnClickListener#onClick(android.view.View)
	 */
	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.search_layout_btn_exe:
			mFilter
					.setEmail(((TextView) findViewById(R.id.search_layout_et_term))
							.getText().toString());
			mHandler.sendEmptyMessage(HANDLE_RELOAD_LIST);
			hideKeyboard();

			break;
		}
		;
	}

	/**
	 * 
	 */
	private final void hideKeyboard() {
		InputMethodManager ims = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
		IBinder token = findViewById(R.id.search_layout_et_term)
				.getWindowToken();
		ims.hideSoftInputFromWindow(token, InputMethodManager.HIDE_NOT_ALWAYS);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * android.widget.TextView.OnEditorActionListener#onEditorAction(android
	 * .widget.TextView, int, android.view.KeyEvent)
	 */
	@Override
	public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {

		switch (event.getKeyCode()) {
		case KeyEvent.KEYCODE_ENTER:
			mFilter.setEmail(v.getText().toString());
			mHandler.sendEmptyMessage(HANDLE_RELOAD_LIST);
			hideKeyboard();
			return true;
		}
		;

		return false;
	}

}